#include "BoundingBox.h"

namespace Rz { namespace Graphics { namespace Math {

BoundingBox BoundingBox::FromMinMax(const Vector3& min, const Vector3& max)
{
	BoundingBox result;

	result.Position = (min + max) * 0.5f;
	result.Extend = max - result.Position;

	return result;
}

Vector3 BoundingBox::GetMinimum() const
{
	return Position - Extend; 
}

Vector3 BoundingBox::GetMaximum() const
{
	return Position + Extend;
}

BoundingBox BoundingBox::operator + (const BoundingBox& aabb) const
{
	Vector3 min1 = GetMinimum();
	Vector3 min2 = aabb.GetMinimum();
	Vector3 max1 = GetMaximum();
	Vector3 max2 = aabb.GetMaximum();

	Vector3 min3(
		min1.X < min2.X ? min1.X : min2.X,
		min1.Y < min2.Y ? min1.Y : min2.Y,
		min1.Z < min2.Z ? min1.Z : min2.Z);

	Vector3 max3(
		max1.X > max2.X ? max1.X : max2.X,
		max1.Y > max2.Y ? max1.Y : max2.Y,
		max1.Z > max2.Z ? max1.Z : max2.Z);

	return BoundingBox::FromMinMax(min3, max3);
}

BoundingBox BoundingBox::operator * (float scale) const
{
	BoundingBox result;

	result.Position = Position;
	result.Extend *= scale;

	return result;
}

void BoundingBox::Transform(const Matrix4& matrix)
{
	Vector3 points[8], min, max;

	points[0] = Position + Vector3(-Extend.X, -Extend.Y, -Extend.Z);
	points[1] = Position + Vector3(Extend.X, -Extend.Y, -Extend.Z);
	points[2] = Position + Vector3(Extend.X, Extend.Y, -Extend.Z);
	points[3] = Position + Vector3(-Extend.X, Extend.Y, -Extend.Z);

	points[4] = Position + Vector3(-Extend.X, -Extend.Y, Extend.Z);
	points[5] = Position + Vector3(Extend.X, -Extend.Y, Extend.Z);
	points[6] = Position + Vector3(Extend.X, Extend.Y, Extend.Z);
	points[7] = Position + Vector3(-Extend.X, Extend.Y, Extend.Z);

	min = max = matrix * points[0];
	for (u32 i = 1; i < 8; ++i)
	{
		points[i] = matrix * points[i];

		if (points[i].X < min.X)
		{
			min.X = points[i].X;
		}
		else if (points[i].X > max.X)
		{
			max.X = points[i].X;
		}

		if (points[i].Y < min.Y)
		{
			min.Y = points[i].Y;
		}
		else if (points[i].Y > max.Y)
		{
			max.Y = points[i].Y;
		}

		if (points[i].Z < min.Z)
		{
			min.Z = points[i].Z;
		}
		else if (points[i].Z > max.Z)
		{
			max.Z = points[i].Z;
		}
	}

	Position = (min + max) * 0.5f;
	Extend = max - Position; 
}

void BoundingBox::GetCornerPoints(Vector3* points) const
{
	Vector3 minim = GetMinimum();
	Vector3 maxim = GetMaximum();

	points[0].Set(minim.X, minim.Y, minim.Z);
	points[1].Set(maxim.X, minim.Y, minim.Z);
	points[2].Set(maxim.X, maxim.Y, minim.Z);
	points[3].Set(minim.X, maxim.Y, minim.Z);
	points[4].Set(minim.X, minim.Y, maxim.Z);
	points[5].Set(maxim.X, minim.Y, maxim.Z);
	points[6].Set(maxim.X, maxim.Y, maxim.Z);
	points[7].Set(minim.X, maxim.Y, maxim.Z);
}

BoundingSphere BoundingBox::GetBoundingSphere() const
{
	BoundingSphere result;

	Memory::Copy(&result.Position, &Position, sizeof(Position));//result.Position = Position;
	result.Radius = Extend.Length();

	return result;
}

} } }

